// Created by Roman Novitsky on 28.05.2009


#region Imported Namespaces


using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;


#endregion


namespace WtfManager.UIManager
{
  public static class MenuManagerExtensions
  {
    #region Methods


    private static bool MapItem(this MenuItem menuItem, MenuItemCommand command)
    {
      if (!menuItem.IsSubMenu())
      {
        if (menuItem.Maps(command))
        {
          SetMenuItemProperties(menuItem, command);

          return true;
        }
      }
      else
      {
        foreach (object item in menuItem.Items)
        {
          var childMenuItem = item as MenuItem;

          if (childMenuItem == null || !childMenuItem.MapItem(command))
          {
            continue;
          }

          return true;
        }
      }

      return false;
    }


    private static void SetMenuItemProperties(MenuItem menuItem, MenuItemCommand command)
    {
      if (menuItem.Header == null)
      {
        menuItem.Header = command.Header;
      }

      menuItem.SetTargetCommandName(command.Header);
      menuItem.Command = command.Command;
      menuItem.Visibility = menuItem.IsHidden() ? Visibility.Collapsed : Visibility.Visible;
      menuItem.CommandTarget = command.CommandTarget;

      if(command.Image != null)
      {
        var image = new Image();
        image.Source = command.Image;
        menuItem.Icon = image;
      }

      if (string.IsNullOrEmpty(command.InputGestureText))
      {
        menuItem.ClearValue(MenuItem.InputGestureTextProperty);
      }
      else
      {
        menuItem.InputGestureText = command.InputGestureText;
      }

      if (string.IsNullOrEmpty(command.ToolTip))
      {
        menuItem.ClearValue(FrameworkElement.ToolTipProperty);
      }
      else
      {
        menuItem.ToolTip = command.ToolTip;
      }

      if (command.CommandParameter == null)
      {
        menuItem.ClearValue(MenuItem.CommandParameterProperty);
      }
      else
      {
        menuItem.CommandParameter = command.CommandParameter;
      }
    }


    internal static void MoveItems(this MenuBase sourceMenu, MenuBase targetMenu)
    {
      while (sourceMenu.Items.Count > 0)
      {
        object item = sourceMenu.Items[0];
        sourceMenu.Items.Remove(item);
        targetMenu.Items.Add(item);
      }
    }


    internal static bool Build(this MenuBase menu, List<MenuItemCommand> commands)
    {
      menu.PrepareToBuild();
      bool hasNewCommands = menu.MapCommands(commands);
      menu.UpdateItemsVisibility();
      menu.UpdateSeparators();

      return hasNewCommands;
    }


    private static void UpdateSeparators(this ItemsControl menu)
    {
      bool hasVisibleItems = false;
      UIElement lastSeparator = null;

      foreach (UIElement item in menu.Items)
      {
        if (item is Separator)
        {
          if (!hasVisibleItems)
          {
            if (lastSeparator != null)
            {
              lastSeparator.Visibility = GetVisibility(false);
              item.Visibility = GetVisibility(true);
            }
            else
            {
              item.Visibility = GetVisibility(false);
            }
          }
          else
          {
            item.Visibility = GetVisibility(true);
          }

          lastSeparator = item;
          hasVisibleItems = false;

          continue;
        }

        if (item is MenuItem)
        {
          UpdateSeparators((ItemsControl)item);
        }

        if (item.Visibility == Visibility.Visible)
        {
          hasVisibleItems = true;
        }
      }

      if (lastSeparator != null)
      {
        lastSeparator.Visibility = GetVisibility(hasVisibleItems);
      }
    }


    private static Visibility GetVisibility(bool visible)
    {
      return visible ? Visibility.Visible : Visibility.Collapsed;
    }


    private static bool UpdateItemsVisibility(this ItemsControl menu)
    {
      bool hasVisibleItems = false;

      foreach (object item in menu.Items)
      {
        var menuItem = item as MenuItem;

        if (menuItem == null)
        {
          if (!(item is Separator))
          {
            hasVisibleItems = true;
          }

          continue;
        }

        if (menuItem.Visibility == Visibility.Visible)
        {
          hasVisibleItems = true;
        }

        if (!menuItem.IsSubMenu())
        {
          continue;
        }

        if (menuItem.IsHidden())
        {
          continue;
        }

        if (menuItem.UpdateItemsVisibility())
        {
          menuItem.Visibility = Visibility.Visible;
          hasVisibleItems = true;
        }

        if (!menuItem.IsPinned())
        {
          continue;
        }

        menuItem.Visibility = Visibility.Visible;
        menuItem.IsEnabled = false;

        hasVisibleItems = true;
      }

      return hasVisibleItems;
    }


    private static bool Maps(this DependencyObject menuItem, MenuItemCommand command)
    {
      return menuItem.GetTargetCommandName() == command.Header;
    }


    private static bool MapCommands(this ItemsControl menu, IEnumerable<MenuItemCommand> commands)
    {
      bool hasNewItems = false;
      MenuItem defaultSubMenu = menu.GetDefaultSubMenu();

      foreach (MenuItemCommand command in commands)
      {
        bool mapped = false;

        foreach (object item in menu.Items)
        {
          var menuItem = item as MenuItem;

          if(menuItem == null)
          {
            continue;
          }

          if (!menuItem.MapItem(command))
          {
            continue;
          }

          mapped = true;
          break;
        }

        if (mapped)
        {
          continue;
        }

        var newItem = new MenuItem();
        SetMenuItemProperties(newItem, command);

        if (defaultSubMenu != null)
        {
          defaultSubMenu.Items.Add(newItem);
        }
        else
        {
          menu.Items.Add(newItem);
        }

        hasNewItems = true;
      }

      return hasNewItems;
    }


    private static void PrepareToBuild(this ItemsControl menu)
    {
      foreach (object item in menu.Items)
      {
        var menuItem = item as MenuItem;

        if (menuItem == null)
        {
          continue;
        }

        menuItem.Visibility = !menuItem.IsPinned() ? Visibility.Collapsed : Visibility.Visible;
        menuItem.PrepareToBuild();
      }
    }


    internal static void PrepareToSaveState(this MenuBase menu)
    {
      PrepareToSave(menu);
    }


    private static void PrepareToSave(ItemsControl menu)
    {
      foreach (object item in menu.Items)
      {
        var menuItem = item as MenuItem;

        if (menuItem == null)
        {
          continue;
        }

        menuItem.ClearValue(UIElement.VisibilityProperty);
        menuItem.ClearValue(MenuItem.CommandParameterProperty);
        menuItem.ClearValue(MenuItem.CommandTargetProperty);
        menuItem.ClearValue(MenuItem.InputGestureTextProperty);
        menuItem.ClearValue(MenuItem.CommandProperty);
        menuItem.ClearValue(UIElement.IsEnabledProperty);
        menuItem.ClearValue(Selector.IsSelectedProperty);
        menuItem.ClearValue(MenuItem.IconProperty);

        PrepareToSave(menuItem);
      }
    }


    private static MenuItem GetDefaultSubMenu(this ItemsControl menu)
    {
      foreach (object item in menu.Items)
      {
        var menuItem = item as MenuItem;

        if (menuItem == null)
        {
          continue;
        }

        MenuItem defaultSubMenu = menuItem.GetDefaultSubMenu();

        if (defaultSubMenu != null)
        {
          return defaultSubMenu;
        }
      }

      return null;
    }


    private static MenuItem GetDefaultSubMenu(this MenuItem menuItem)
    {
      if (MenuManager.GetIsDefault(menuItem))
      {
        return menuItem;
      }

      foreach (object item in menuItem.Items)
      {
        if (!(item is MenuItem))
        {
          continue;
        }

        var childMenuItem = (MenuItem)item;
        MenuItem defaultSubMenu = childMenuItem.GetDefaultSubMenu();

        if (defaultSubMenu != null)
        {
          return defaultSubMenu;
        }
      }

      return null;
    }


    private static bool IsSubMenu(this MenuItem menuItem)
    {
      return menuItem.Role == MenuItemRole.SubmenuHeader || menuItem.Role == MenuItemRole.TopLevelHeader;
    }


    private static bool IsHidden(this DependencyObject menuItem)
    {
      return MenuManager.GetIsHidden(menuItem);
    }


    private static bool IsPinned(this DependencyObject menuItem)
    {
      return MenuManager.GetIsPinned(menuItem);
    }


    private static string GetTargetCommandName(this DependencyObject menuItem)
    {
      return MenuManager.GetTargetCommandName(menuItem);
    }


    private static void SetTargetCommandName(this DependencyObject menuItem, string commandName)
    {
      MenuManager.SetTargetCommandName(menuItem, commandName);
    }


    private static MenuItem CreateDefaultSubMenu(this ItemsControl menu)
    {
      var defaultSubMenu = new MenuItem();
      defaultSubMenu.Header = "Other";
      MenuManager.SetIsDefault(defaultSubMenu, true);

      menu.Items.Add(defaultSubMenu);

      return defaultSubMenu;
    }


    #endregion
  }
}